module sw.bugtracking.bugtrackingdat;

version(BugTracking):
import std.string, nazo.file, std.date, std.conv, nazo.date;
import sw.bugtracking.bugtrackingdatlist, sw.siteconfig, sw.name;

static struct Status{
  static enum list{
    Unconfirmed,
    Confirmed,
    WontFix,
    Duplicate,
    Invalid,
    VerifiedCheckedin,
    VerifiedReleased,
    PatchCheckedin,
    PatchBackout,
    Released,
  }
  static string[] nameList=[list.Unconfirmed:"未確認",list.Confirmed:"確認済",list.WontFix:"修正予定無し",list.Duplicate:"重複",list.Invalid:"不正",list.VerifiedCheckedin:"別のバグでチェックイン済",list.VerifiedReleased:"別のバグでリリース済",list.PatchCheckedin:"チェックイン済",list.PatchBackout:"チェックイン取消",list.Released:"リリース済"];//未ローカライズ
  static string[] valueList=[list.Unconfirmed:"unconfirmed",list.Confirmed:"confirmed",list.WontFix:"wontfix",list.Duplicate:"duplicate",list.Invalid:"invalid",list.VerifiedCheckedin:"verifiedcheckedin",list.VerifiedReleased:"verifiedreleased",list.PatchCheckedin:"patchcheckedin",list.PatchBackout:"patchbackout",list.Released:"released"];
  static list[] initialList=[list.Unconfirmed,list.Confirmed,list.VerifiedCheckedin,list.PatchCheckedin,list.VerifiedReleased,list.Released];
  static list getId(string val){
    foreach(uint index,string value;valueList){
      if(value==val)return cast(list)index;
    }
    assert(0);
  }
}

static struct SubStatus{
  static enum list{
    WaitConfirm,
    WaitOtherBug,
    WaitDiscussion,
    TestCaseWanted,
    TestCaseCorrectWanted,
    PatchWanted,
    PatchCorrectWanted,
    PatchTesterWanted,
    PatchReviewWanted,
    CheckinWanted,
    WaitRelease,
    Finished,
    Leaving,
  }
  static string[] nameList=[list.WaitConfirm:"確認待ち",list.WaitOtherBug:"他のバグの修正待ち",list.WaitDiscussion:"議論待ち",list.TestCaseWanted:"テストケース待ち",list.TestCaseCorrectWanted:"テストケースの修正待ち",list.PatchWanted:"パッチ待ち",list.PatchCorrectWanted:"パッチの修正待ち",list.PatchTesterWanted:"テスターのテスト待ち",list.PatchReviewWanted:"パッチの評価待ち",list.CheckinWanted:"チェックイン待ち",list.WaitRelease:"リリース待ち",list.Finished:"完了",list.Leaving:"放置"];//未ローカライズ
  static string[] valueList=[list.WaitConfirm:"waitconfirm",list.WaitOtherBug:"waitotherbug",list.WaitDiscussion:"waitdiscussion",list.TestCaseWanted:"testcasewanted",list.TestCaseCorrectWanted:"testcasecorrectwanted",list.PatchWanted:"patchwanted",list.PatchCorrectWanted:"patchcorrectwanted",list.PatchTesterWanted:"patchtesterwanted",list.PatchReviewWanted:"patchreviewwanted",list.CheckinWanted:"checkinwanted",list.WaitRelease:"waitrelease",list.Finished:"finished",list.Leaving:"leaving"];
  static list[] initialList=[list.WaitConfirm,list.WaitOtherBug,list.WaitDiscussion,list.PatchWanted,list.PatchCorrectWanted,list.PatchTesterWanted,list.CheckinWanted,list.WaitRelease,list.Leaving];
  static list getId(string val){
    foreach(uint index,string value;valueList){
      if(value==val)return cast(list)index;
    }
    assert(0);
  }
}

struct Comment{
  string name;
  string message;
  d_time datetime;
  void newComment(SiteConfig* cfg,string message){
    newComment(cfg,"",message);
  }
  void newComment(SiteConfig* cfg,string name,string message){
    this.name=getNameWithTrip(cfg,name);
    this.message=message;
    this.datetime=getUTCtime();
  }
}

/**
 * Bugのdat
 */
class BugDat{
  BugList buglist;
  this(BugList buglist){
    this.buglist=buglist;
    this.bugnum=buglist.getList.length;
  }
  this(BugList buglist,uint bugnum){
    this.buglist=buglist;
    load(bugnum);
  }
  Status.list status=Status.list.Unconfirmed;
  SubStatus.list subStatus=SubStatus.list.WaitConfirm;
  uint bugnum;
  string summary;
  string details;
  string category;
  Comment*[] comments;
  void flash(){
    if(!exists("bug")){
      mkdir("bug");
    }
    string[] lst=buglist.getList;
    if(lst.length<=bugnum)
      buglist.append(encode(summary)~","~encode(category)~","~encode(details)~","~Status.valueList[status]~","~SubStatus.valueList[subStatus]);
    else{
      lst[bugnum]=encode(summary)~","~encode(category)~","~encode(details)~","~Status.valueList[status]~","~SubStatus.valueList[subStatus];
      buglist.setList(lst);
    }
    string commentsStr;
    foreach(Comment* cmt;comments){
      commentsStr~=encode(cmt.name)~","~encode(cmt.message)~","~std.string.toString(toUtime(cmt.datetime))~"\n";
    }
    write("bug/"~std.string.toString(bugnum)~".dat",commentsStr);
  }
  private void load(uint bugnum){
    this.bugnum=bugnum;
    string[] bls=buglist.getList;
    if(0>bugnum||bls.length<=bugnum)throw new Exception("Invalid bug number("~std.string.toString(bugnum)~")");
    string[] status=bls[bugnum].split(",");
    summary=decode(status[0]);
    category=decode(status[1]);
    details=decode(status[2]);
    this.status=Status.getId(status[3]);
    this.subStatus=SubStatus.getId(status[4]);
    string[] cmtDat=.splitlines(cast(string)read("bug/"~std.string.toString(bugnum)~".dat"));
    foreach(string cmt;cmtDat){
      if(cmt=="")continue;
      string[] cmtLine=cmt.split(",");
      comments~=new Comment;
      comments[$-1].name=decode(cmtLine[0]);
      comments[$-1].message=decode(cmtLine[1]);
      comments[$-1].datetime=getUTCtimeFromUtime(.toInt(cmtLine[2]));
    }
  }
  d_time lastModified(){
    return getLastWriteTime("bug/"~std.string.toString(bugnum)~".dat");
  }
  private string encode(string str){
    return str.replace("\\","\\\\").replace(",","\\c").replace("\n","\\n").replace("\r","\\r");
  }
  private string decode(string str){
    return str.replace("\\n","\n").replace("\\r","\r").replace("\\c",",").replace("\\\\","\\");
  }
}
